{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XGCdmDAKpLuf"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors.\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "GF4d1XplpLGF"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W1L3zJP6pPGD"
      },
      "source": [
        "# Состязательный пример использования FGSM\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/generative/adversarial_fgsm\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Смотрите на TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ru/tutorials/generative/adversarial_fgsm.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запустите в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ru/tutorials/generative/adversarial_fgsm.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучайте код на GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ru/tutorials/generative/adversarial_fgsm.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Скачайте ноутбук</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d3874b352184"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8dn1-g8BpPDx"
      },
      "source": [
        "В этом руководстве показан пример *состязательной атаки* с использованием Fast Gradient Sign Method (FGSM), как описано в [Объяснение и использование состязательных атак](https://arxiv.org/abs/1412.6572) Гудфеллоу *и др.*. Это была одна из первых и самых популярных атак с целью обмануть нейронную сеть.\n",
        "\n",
        "## Что такое состязательный пример?\n",
        "\n",
        "Состязательные атаки - это специализированные входные данные, созданные с целью запутать нейронную сеть, что приводит к неправильной классификации заданного входа. Эти специализированные входные данные неразличимы для человеческого глаза, но сеть не может правильно идентифицировать содержимое изображения. \n",
        "Существует несколько типов таких атак, в данном руководстве мы будем рассматривать атаку *белого ящика* методом FGSM, цель которой - гарантировать неправильную классификацию. Атака *белого ящика* - это когда злоумышленник имеет полный доступ к атакуемой модели. Один из самых известных примеров состязательного изображения, показанный ниже, взят из вышеупомянутой статьи.\n",
        "\n",
        "![Пример состязательности](images/adversarial_example.png)\n",
        "\n",
        "В этом примере, в исходное изображение *панды* злоумышленник добавляет небольшой шум(искажения), в результате чего модель с высокой степенью вероятности маркирует это изображение как *гиббон*. Ниже объясняется процесс добавления этих шумов.\n",
        "\n",
        "\n",
        "\n",
        "## FGSM\n",
        "\n",
        "FGSM работает с использованием градиентов нейронной сети для создания состязательной атаки. Метод применяет градиенты потерь к входному изображению, чтобы создать новое изображение, которое увеличит потери. Этот новый образ называется состязательным. Это можно представить с помощью следующего выражения:\n",
        "$$adv\\_x = x + \\epsilon*\\text{sign}(\\nabla_xJ(\\theta, x, y))$$\n",
        "\n",
        "где\n",
        "\n",
        "* adv_x: состязательное изображение(результат).\n",
        "* x: Исходное входное изображение.\n",
        "* y: Исходная метка входного изображения.\n",
        "* $\\epsilon$: множитель для обеспечения небольшого шума.\n",
        "* $\\theta$: Параметры модели.\n",
        "* $J$: Потери.\n",
        "\n",
        "Интересным свойством является тот факт, что градиенты берутся по отношению ко входному изображению. Это сделано для того,чтобы создать изображение, которое увеличит потери. Для этого нужно определить, насколько каждый пиксель изображения влияет на величину потерь, и соответственно добавить шум. Это работает довольно быстро, потому что всего-лишь нужно посчитать, как каждый входной пиксель влияет на потери, используя правило цепочки и находя требуемые градиенты. Поскольку модель больше не обучается, параметры модели остаются постоянными, и таким образом, градиент не расчитывается относительно обучаемых переменных. Единственная цель - обмануть уже обученную модель.\n",
        "\n",
        "Итак, давайте попробуем обмануть предварительно обученную модель. В этом руководстве используется модель [MobileNetV2](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/applications/MobileNetV2), предварительно обученная на [ImageNet](http://www.image-net.org/)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vag2WYR6yTOC"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "import matplotlib as mpl\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "mpl.rcParams['figure.figsize'] = (8, 8)\n",
        "mpl.rcParams['axes.grid'] = False"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wiTHY8dqxzx7"
      },
      "source": [
        "Загрузим предварительно обученную модель MobileNetV2 и имена классов ImageNet."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nqhk2vYx6Ag0"
      },
      "outputs": [],
      "source": [
        "pretrained_model = tf.keras.applications.MobileNetV2(include_top=True,\n",
        "                                                     weights='imagenet')\n",
        "pretrained_model.trainable = False\n",
        "\n",
        "# ImageNet метки\n",
        "decode_predictions = tf.keras.applications.mobilenet_v2.decode_predictions"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f2cLrJH0zpfC"
      },
      "outputs": [],
      "source": [
        "# Вспомогательная функция для предварительной обработки изображения, чтобы его можно было ввести в MobileNetV2.\n",
        "def preprocess(image):\n",
        "  image = tf.cast(image, tf.float32)\n",
        "  image = tf.image.resize(image, (224, 224))\n",
        "  image = tf.keras.applications.mobilenet_v2.preprocess_input(image)\n",
        "  image = image[None, ...]\n",
        "  return image\n",
        "\n",
        "# Вспомогательная функция для извлечения меток из вектора вероятности\n",
        "def get_imagenet_label(probs):\n",
        "  return decode_predictions(probs, top=1)[0][0]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iEZaMVFgSUA-"
      },
      "source": [
        "## Исходное изображение\n",
        "Давайте воспользуемся образцом изображения [Лабрадора ретривера](https://commons.wikimedia.org/wiki/File:YellowLabradorLooking_new.jpg) Мирко [CC-BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/) из Wikimedia Common и создадим на его основе состязательные примеры. Первым шагом является предварительная обработка, чтобы его можно было использовать в качестве входных данных для модели MobileNetV2."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wpYrQ4OQSYWk"
      },
      "outputs": [],
      "source": [
        "image_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')\n",
        "image_raw = tf.io.read_file(image_path)\n",
        "image = tf.image.decode_image(image_raw)\n",
        "\n",
        "image = preprocess(image)\n",
        "image_probs = pretrained_model.predict(image)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mvPlta_uSbuI"
      },
      "source": [
        "Посмотрим на изображение."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "99Jc-SNoSZot"
      },
      "outputs": [],
      "source": [
        "plt.figure()\n",
        "plt.imshow(image[0]*0.5+0.5) # To change [-1, 1] to [0,1]\n",
        "_, image_class, class_confidence = get_imagenet_label(image_probs)\n",
        "plt.title('{} : {:.2f}% Confidence'.format(image_class, class_confidence*100))\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kElVTbF690CF"
      },
      "source": [
        "## Создание состязательного образа\n",
        "\n",
        "### Реализация метода быстрого градиента признаков\n",
        "Первый шаг - создать шум, который будет использоваться для искажения исходного изображения, что приведет к появлению состязательного изображения. Как уже упоминалось, для этой задачи градиенты берутся по отношению к изображению."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FhZxlOnuBCVr"
      },
      "outputs": [],
      "source": [
        "loss_object = tf.keras.losses.CategoricalCrossentropy()\n",
        "\n",
        "def create_adversarial_pattern(input_image, input_label):\n",
        "  with tf.GradientTape() as tape:\n",
        "    tape.watch(input_image)\n",
        "    prediction = pretrained_model(input_image)\n",
        "    loss = loss_object(input_label, prediction)\n",
        "\n",
        "  # Получаем градиенты потерь относительно входного изображения.\n",
        "  gradient = tape.gradient(loss, input_image)\n",
        "  # Получаем признаки градиентов, чтобы создать шум\n",
        "  signed_grad = tf.sign(gradient)\n",
        "  return signed_grad"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RbuftX0eSlDQ"
      },
      "source": [
        "Возникающие шумы также можно визуализировать."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rVjnb6M7Smv4"
      },
      "outputs": [],
      "source": [
        "# Получаем метку входного изображения.\n",
        "labrador_retriever_index = 208\n",
        "label = tf.one_hot(labrador_retriever_index, image_probs.shape[-1])\n",
        "label = tf.reshape(label, (1, image_probs.shape[-1]))\n",
        "\n",
        "perturbations = create_adversarial_pattern(image, label)\n",
        "plt.imshow(perturbations[0]*0.5+0.5); # Изменяем [-1, 1] на [0,1]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DKKSFHjwCyQH"
      },
      "source": [
        "Давайте попробуем разные значения epsilon и посмотрим на полученное изображение. Вы заметите, что по мере увеличения значения epsilon, становится легче обмануть сеть. Однако в результате увеличения epsilon, шумы становятся более заметными."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "dBtG0Kl5SspV"
      },
      "outputs": [],
      "source": [
        "def display_images(image, description):\n",
        "  _, label, confidence = get_imagenet_label(pretrained_model.predict(image))\n",
        "  plt.figure()\n",
        "  plt.imshow(image[0]*0.5+0.5)\n",
        "  plt.title('{} \\n {} : {:.2f}% Confidence'.format(description,\n",
        "                                                   label, confidence*100))\n",
        "  plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3DA8g-Zp69J4"
      },
      "outputs": [],
      "source": [
        "epsilons = [0, 0.01, 0.1, 0.15]\n",
        "descriptions = [('Epsilon = {:0.3f}'.format(eps) if eps else 'Input')\n",
        "                for eps in epsilons]\n",
        "\n",
        "for i, eps in enumerate(epsilons):\n",
        "  adv_x = image + eps*perturbations\n",
        "  adv_x = tf.clip_by_value(adv_x, -1, 1)\n",
        "  display_images(adv_x, descriptions[i])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fxt5VfnXHQT6"
      },
      "source": [
        "## Следующие шаги\n",
        "\n",
        "Теперь, когда вы знаете о состязательных атаках, попробуйте их использовать на разных наборах данных и на разных архитектурах. Вы можете создать и обучить свою собственную модель, а затем попытаться обмануть ее, используя тот же метод. Вы также можете посмотреть, как меняется уверенность в предсказаниях при изменении epsilon.\n",
        "\n",
        "Несмотря на свою мощь, атака, показанная в этом руководстве, была только началом исследования состязательных атак, и с тех пор было опубликовано несколько статей, в которых создавались более мощные атаки. Помимо состязательных атак, исследования также привели к созданию средств защиты, направленных на создание надежных моделей машинного обучения. Вы можете просмотреть этот [обзорный документ](https://arxiv.org/abs/1810.00069) для получения исчерпывающего списка состязательных атак и средств защиты.\n",
        "\n",
        "Для многих других реализаций состязательных атак и защит вы можете просмотреть библиотеку состязательных примеров [CleverHans](https://github.com/tensorflow/cleverhans)."
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "adversarial_fgsm.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
